/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ssp.event;

import java.util.ArrayList;
import java.util.List;
import ssp.agent.Agent_v1;
import ssp.agent.QualityDemand;
import ssp.net.NWcomponent;
import ssp.net.NWcomponentManager;
import ssp.service.FrontendServer;
import ssp.service.FrontendServerManager;
import ssp.service.ServiceEntry;
import ssp.service.ServiceEntryPool;
import ssp.strategy.agent.AgentStrategySelector;
import ssp.topology.Node;
import ssp.utility.MersenneTwisterRNG;
import ssp.utility.PropertyManager;

/**
 * ユーザからの要求イベントを表すクラス
 * @author yamazaki
 */
public class ArrivalEvent extends Event {
    
    /**
     * 残りイベント回数
     */
    int num;
    
    /**
     * コンストラクタ
     * @param eventTime イベント時間
     * @param num  イベント回数
     */
    public ArrivalEvent(double eventTime, int num) {
        super(eventTime);
        this.num = num;
    }

    @Override
    public void fire() {
        
        //ユーザの属するネットワークコンポーネント
        NWcomponent userNode = NWcomponentManager.getINSTANCE().getRandomNWcomponent();
        
        //サービスID
        int typeID = MersenneTwisterRNG.getINSTANCE().nextInt(PropertyManager.getValueAsInteger("ServiceEntryTypeNum"));
        
        //サービス品質
        List<Integer> serviceRequirement = new ArrayList<>();
        for(int i=0; i<PropertyManager.getValueAsInteger("ServiceRequirementNum") ; i++) {
            serviceRequirement.add(MersenneTwisterRNG.getINSTANCE().nextInt(ServiceEntryPool.getINSTANCE().MaxQoSindex(typeID, i)));
        }
        
        //サービス時間
        double serviceTimeLength = PropertyManager.getValueAsInteger("ServiceTimeBase") * MersenneTwisterRNG.getINSTANCE().nextDouble();
        
        //ネットワーク品質
        List<Double> networkRequirement = new ArrayList<>();
        int networkRequirementNum = PropertyManager.getValueAsInteger("NetworkRequirementNum");
        int averageOfNetworkComponentNum = PropertyManager.getValueAsInteger("AverageOfNetworkComponentNum");
        int networkRequirementRange = PropertyManager.getValueAsInteger("NetworkRequirementRange");
        double networkRequirementMax = PropertyManager.getValueAsDouble("NetworkRequirementMax");
        for(int i=0; i<networkRequirementNum ; i++) {
            double sum = 0.0;
            double delta = (networkRequirementMax - 1.0) / (double)networkRequirementRange;
            for(int j=0; j<averageOfNetworkComponentNum ; j++) {
                sum += MersenneTwisterRNG.getINSTANCE().nextInt(networkRequirementRange) * delta;
            }
            networkRequirement.add(sum);
        }
        
        //ネットワーク帯域
        double bandwidth = PropertyManager.getValueAsDouble("BandwidthUnit") * (MersenneTwisterRNG.getINSTANCE().nextInt(PropertyManager.getValueAsInteger("BandwidthBase")) + 1);
        
        //ユーザの要求
        QualityDemand qualityRequirement = new QualityDemand(typeID, serviceRequirement, serviceTimeLength, networkRequirement, bandwidth);
        
        //フロントエンドサーバの探索範囲
        int hopNum = PropertyManager.getValueAsInteger("SearchHopNum");
        int chargeNum = PropertyManager.getValueAsInteger("SearchFrontendServerNum");
        
        //new Agent_v1(userNode, qualityRequirement, AgentStrategySelector.getAgent_v1_Strategy()).createServicePath(hopNum, chargeNum);
        
//        //探索に失敗した場合
//        if(!new Agent_v1(userNode, qualityRequirement, AgentStrategySelector.getAgent_v1_Strategy()).createServicePath(hopNum, chargeNum)) {
//            LoggingManager.logging(Level.INFO, "Fail to make service.", new Throwable());
//        }
        
        if(PropertyManager.getValueAsBoolean("AlwaysUpdateDatabase")) {
            
            //到着ごとにDBを更新
            for (FrontendServer frontendServer : FrontendServerManager.getINSTANCE().getFrontendServerList()) {
                for (ServiceEntry entry : frontendServer.getServiceEntryList()) {
                    frontendServer.updateServiceMapForAvailabillity(entry, getEventTime());
                }
            }

            for (NWcomponent nwComponent : NWcomponentManager.getINSTANCE().getNWcomponentList()) {
                List<Node> neighbors = nwComponent.getNeighborNWcomponents();
                int neighborsSize = neighbors.size();
                for (int i=0 ; i<neighborsSize ; i++) {
                    for (int j=i ; j<neighborsSize ; j++) {
                        nwComponent.updateNWmapForAvailability(neighbors.get(i), neighbors.get(j), getEventTime());
                    }
                }
            }
            
        }
        
        //エージェントを送出
        new Agent_v1(userNode, qualityRequirement, AgentStrategySelector.getAgent_v1_Strategy()).createServicePath(hopNum, chargeNum);
        
        EventQueue eventQueue = EventQueue.getINSTANCE();
        if(PropertyManager.getValueAsInteger("ArrivalNum") - num == PropertyManager.getValueAsInteger("TransientNum")) {
            eventQueue.startLogging();
        }
        
        //次のイベントを生成
        if(num != 1) {
            eventQueue.addEvent(new ArrivalEvent(eventQueue.getCurrentTime() + MersenneTwisterRNG.getINSTANCE().nextExponetial(PropertyManager.getValueAsDouble("ArraivalRate")), num-1));
        } else {
            
            //(メイン)ループ脱出のためのdumyEvent
            eventQueue.addEvent(new Event(-1.0) {

                @Override
                public void fire() {
                    ;
                }
            });
            
        }
    }
    
}
